// Package main implements a server for Greeter service.
package main

import (
	"context"
	"fmt"
	"io"
	"math/rand"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"

	"google.golang.org/grpc"

	pb "gitee.com/aoliang/graceful/graceful_update_grpc/proto"
)

// helloWorld is used to implement helloworld.GreeterServer.
type helloWorld struct{}

// SayHello implements helloworld.GreeterServer
func (s *helloWorld) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	time.Sleep(10 * time.Millisecond)
	time.Sleep(time.Duration(rand.Intn(10)+5) * time.Millisecond)
	return &pb.HelloReply{Message: "Hello v2 " + in.Name}, nil
}

func (s *helloWorld) SayStreamHello(stream pb.Greeter_SayStreamHelloServer) error {
	for {
		in, err := stream.Recv()
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}

		if err := stream.Send(&pb.HelloReply{Message: fmt.Sprintf("hello v2 %s", in.Name)}); err != nil {
			return err
		}
	}
}

func main() {
	fmt.Println("pid:", pid)

	const addr = "0.0.0.0:50051"

	grpcServer := grpc.NewServer()
	pb.RegisterGreeterServer(grpcServer, &helloWorld{})

	s := &Server{s: grpcServer}
	s.start(addr)

	ch := make(chan os.Signal, 10)
	signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL, syscall.SIGUSR2)
	for {
		sig := <-ch
		switch sig {
		case syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL:
			//监听平滑关闭信号
			fmt.Println("receive shutdown signal")
			s.gracefulShutdown()
			return
		case syscall.SIGUSR2:
			//监听热重启信号
			fmt.Println("receive restart signal")
			s.gracefulRestart()
		}
	}
}

const listenFds = "LISTEN_FDS"

var (
	cwd, _     = os.Getwd()
	pid        = os.Getpid()
	ppid       = os.Getppid()
	didInherit = os.Getenv(listenFds) != "" //如果父进程fork出来，则有此环境变量
)

type Server struct {
	s           *grpc.Server
	active      []net.Listener
	inherited   []net.Listener
	mutex       sync.Mutex
	inheritOnce sync.Once
}

func (s *Server) start(addr string) {
	//获取和父进程相同的监听器的fd
	if err := s.inheritListener(); err != nil {
		panic(err)
	}

	//获取监听器，可能是父进程相同的fd，也可能是自己创建的。
	l, err := s.listen(addr)
	if err != nil {
		panic(err)
	}

	go func() {
		if err := s.s.Serve(l); err != nil {
			fmt.Println(err)
		}
	}()

	// 关闭父进程
	if didInherit && ppid != 1 {
		if err := syscall.Kill(ppid, syscall.SIGTERM); err != nil {
			panic(err)
		}
	}
}

func (s *Server) inheritListener() error {
	var retErr error
	s.inheritOnce.Do(func() {
		s.mutex.Lock()
		defer s.mutex.Unlock()

		if !didInherit {
			return
		}

		count, err := strconv.Atoi(os.Getenv(listenFds))
		if err != nil {
			retErr = fmt.Errorf("found invalid count value: %s=%s", listenFds, os.Getenv(listenFds))
			return
		}
		if count == 0 {
			return
		}

		//子进程继承父进程的文件描述表，前三个是Stdin Stdout、StdErr 即0、1、2
		for i := 3; i < 3+count; i++ {
			file := os.NewFile(uintptr(i), "listener")
			l, err := net.FileListener(file)
			if err != nil {
				file.Close()
				retErr = fmt.Errorf("error inheriting socket fd %d: %s", i, err)
				return
			}
			if err := file.Close(); err != nil {
				retErr = fmt.Errorf("error closing inherited socket fd %d: %s", i, err)
				return
			}
			s.inherited = append(s.inherited, l)
		}
	})
	return retErr
}

func (s *Server) listen(addr string) (net.Listener, error) {
	laddr, err := net.ResolveTCPAddr("tcp", addr)
	if err != nil {
		return nil, err
	}

	s.mutex.Lock()
	defer s.mutex.Unlock()

	//查找
	for i, l := range s.inherited {
		if l == nil {
			continue
		}
		if isSameAddr(l.Addr(), laddr) {
			s.inherited[i] = nil
			s.active = append(s.active, l)
			return l.(*net.TCPListener), nil
		}
	}

	//继承的fd中没有监听此地址
	l, err := net.ListenTCP("tcp", laddr)
	if err != nil {
		return nil, err
	}
	s.active = append(s.active, l)
	return l, nil

}

func isSameAddr(a1, a2 net.Addr) bool {
	if a1.Network() != a2.Network() {
		return false
	}
	a1s := a1.String()
	a2s := a2.String()
	if a1s == a2s {
		return true
	}

	const ipv6prefix = "[::]"
	a1s = strings.TrimPrefix(a1s, ipv6prefix)
	a2s = strings.TrimPrefix(a2s, ipv6prefix)
	const ipv4prefix = "0.0.0.0"
	a1s = strings.TrimPrefix(a1s, ipv4prefix)
	a2s = strings.TrimPrefix(a2s, ipv4prefix)
	return a1s == a2s
}

func (s *Server) activeListeners() ([]net.Listener, error) {
	s.mutex.Lock()
	ls := make([]net.Listener, len(s.active))
	copy(ls, s.active)
	s.mutex.Unlock()
	return ls, nil
}

func (s *Server) gracefulShutdown() {
	fmt.Println("start graceful shutdown", pid)
	s.s.GracefulStop()
	fmt.Println("stop graceful shutdown", pid)
}

func (s *Server) gracefulRestart() {
	fmt.Println("start graceful restart", pid)
	err := s.startChildProcess()
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println("stop graceful restart", pid)
}

func (s *Server) startChildProcess() error {
	listeners, err := s.activeListeners()
	if err != nil {
		return err
	}

	//获取当前所有监听器的fd
	files := make([]*os.File, len(listeners))
	for i, l := range listeners {
		files[i], err = l.(*net.TCPListener).File() //强制转换，此处不太合理
		if err != nil {
			return err
		}
		defer files[i].Close()
	}

	argv0, err := exec.LookPath(os.Args[0])
	if err != nil {
		return err
	}

	env := []string{fmt.Sprintf("%s=%d", listenFds, len(listeners))}
	for _, v := range os.Environ() {
		if !strings.HasPrefix(v, listenFds) {
			env = append(env, v)
		}
	}

	allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...)

	fmt.Println("cwd", cwd)
	fmt.Println("env", env)
	fmt.Println("fd", allFiles)

	_, err = os.StartProcess(argv0, os.Args, &os.ProcAttr{
		Dir:   cwd,
		Env:   env,
		Files: allFiles,
	})
	return err
}
